//
// Copyright (c) 2002-2009 "Neo Technology,"
//     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// This file is part of Neo4j.
// 
// Neo4j is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
// 
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
#region Using Directives

using org.neo4j.graphdb;
using Direction = org.neo4j.graphdb.Direction;
using Node = org.neo4j.graphdb.Node;
using RelationshipType = org.neo4j.graphdb.RelationshipType;
using ReturnableEvaluator = org.neo4j.graphdb.ReturnableEvaluator;
using StopEvaluator = org.neo4j.graphdb.StopEvaluator;
using Traverser = org.neo4j.graphdb.Traverser;

#endregion
namespace org.neo4j.kernel.impl.traversal
{
    /// <summary>
    ///  The factory for <seealso cref="Traverser"/> instances. The TraverserFactory is
    /// responsible for creating and parameterizing traversers. It has a single
    /// <seealso cref="#createTraverser createTraverser()"/> factory method which is overloaded
    /// with a number of different default parameters, which are described briefly
    /// below:
    /// <UL>
    /// <LI><CODE>type</CODE> - which is either one of
    /// <seealso cref="Traverser#DEPTH_FIRST"/> and <seealso cref="Traverser#BREADTH_FIRST"/>. This
    /// parameter decides the strategy used when traversering the node space.
    /// <LI><CODE>startNode</CODE> - the starting node for the traverser.
    /// <LI><CODE>traversableRels</CODE> - a list of the relationship types that
    /// will be traversed.
    /// <LI><CODE>traversableDirs</CODE> - a list of directions in which the
    /// traversable relationships will be traversed. The values in this array should
    /// be either one of <seealso cref="Traverser#DIRECTION_FORWARDS"/>,
    /// <seealso cref="Traverser#DIRECTION_BACKWARDS"/> or <seealso cref="Traverser#DIRECTION_BOTH"/>.
    /// This parameter is ignored in the case of non-directed relationships.
    /// <LI><CODE>stopEvaluator</CODE> - the visitor that is used to evaluate
    /// whether the traverser should stop before traversing a specific node. Use this
    /// parameter to limit the size of the traversal.
    /// <LI><CODE>returnableEvaluator</CODE> - the visitor that is used to
    /// evaluate whether the traverser should return a specific node. Use this
    /// parameter to affect the selection of nodes from the traversal.
    /// </UL>
    /// The factory methods treat all parameters as immutable and thus do not modify
    /// them. This guarantees that the client can optimize by reusing parameters
    /// between invocations of <CODE>createTraverser()</CODE>. </summary>
	public sealed class InternalTraverserFactory
    {
        #region Class Constructors

        /// <summary>
        /// 
        /// </summary>
        public InternalTraverserFactory()
        {
        }

        #endregion

        #region Implementations

        ///<summary>
        ///  Creates a parameterized traverser, using non-default values on all
        ///parameters. 
        /// </summary>
        ///<param name="type">
        ///           the traverser type, either one of
        ///           <seealso cref="Traverser#DEPTH_FIRST"/> and
        ///           <seealso cref="Traverser#BREADTH_FIRST"/> </param>
        ///<param name="traversalOrder"></param>
        ///<param name="startNode">
        ///           the start node for the new traverser </param>
        ///<param name="traversableRels">
        ///           the relationship types that the new traverser will traverse </param>
        ///<param name="traversableDirs">
        ///           the directions that the traversable relationships will be
        ///           traversed in </param>
        ///<param name="stopEvaluator">
        ///           the client hook for limiting the traversal size </param>
        ///<param name="returnableEvaluator">
        ///           the client hook for evaluating nodes before they are returned </param>
        ///<exception cref="IllegalArgumentException">
        ///            if one or more of the parameters are invalid </exception>
        public Traverser CreateTraverser(Order traversalOrder, Node startNode, RelationshipType[] traversableRels, Direction[] traversableDirs, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator)
        {
            if (traversableRels == null || traversableDirs == null)
            {
                throw new IllegalArgumentException("Using this constructor requires that traversable " + "relationships array and traversable directions array " + "isn't null: travRels[" + Arrays.ToString(traversableRels) + "] " + "travDirs[" + Arrays.ToString(traversableDirs) + "]");
            }
            if (traversableRels.Length != traversableDirs.Length)
            {
                throw new IllegalArgumentException("Length of traversable relationships array isn't equal to " + "length of traversable directions array: " + "travRels.length[" + traversableRels.Length + "] != " + "travDirs.length[" + traversableDirs.Length + "]");
            }
            if (traversalOrder == Order.BREADTH_FIRST)
            {
                return new BreadthFirstTraverser(startNode, traversableRels, traversableDirs, null, null, stopEvaluator, returnableEvaluator, null);
            }
            else if (traversalOrder == Order.DEPTH_FIRST)
            {
                return new DepthFirstTraverser(startNode, traversableRels, traversableDirs, null, null, stopEvaluator, returnableEvaluator, null);
            }
            else
            {
                throw new IllegalArgumentException("Unknown traverser type: " + traversalOrder);
            }
        }
        ///<summary> 
        /// Creates a parameterized traverser which traverses all relationships in
        ///the <seealso cref="Traverser#DIRECTION_BOTH default"/> direction. 
        /// </summary>
        ///<param name="type">
        ///           the traverser type, either one of
        ///           <seealso cref="Traverser#DEPTH_FIRST"/> and
        ///           <seealso cref="Traverser#BREADTH_FIRST"/> </param>
        ///<param name="traversalOrder"></param>
        ///<param name="startNode">
        ///           the start node for the new traverser </param>
        ///<param name="traversableRels">
        ///           the relationship types that the new traverser will traverse </param>
        ///<param name="stopEvaluator">
        ///           the client hook for limiting the traversal size </param>
        ///<param name="returnableEvaluator">
        ///           the client hook for evaluating nodes before they are returned </param>
        ///<exception cref="IllegalArgumentException">
        ///            if one or more of the parameters are invalid </exception>
        public Traverser CreateTraverser(Order traversalOrder, Node startNode, RelationshipType[] traversableRels, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator)
        {
            if (traversalOrder == Order.BREADTH_FIRST)
            {
                return new BreadthFirstTraverser(startNode, traversableRels, null, null, null, stopEvaluator, returnableEvaluator, null);
            }
            else if (traversalOrder == Order.DEPTH_FIRST)
            {
                return new DepthFirstTraverser(startNode, traversableRels, null, null, null, stopEvaluator, returnableEvaluator, null);
            }
            else
            {
                throw new IllegalArgumentException("Unknown traverser type: " + traversalOrder);
            }
        }
        ///<summary> 
        /// Creates a parameterized traverser which traverses a single relationship
        ///type. </summary>
        ///<param name="type">
        ///           the traverser type, either one of
        ///           <seealso cref="Traverser#DEPTH_FIRST"/> and
        ///           <seealso cref="Traverser#BREADTH_FIRST"/> </param>
        ///*
        /// <param name="traversalOrder"></param>
        /// <param name="startNode">
        ///           the start node for the new traverser </param>
        ///<param name="traversableRel">
        ///           the relationship type that the new traverser will traverse </param>
        ///<param name="direction">
        ///           the direction the <CODE>traversableRel</CODE> will be
        ///           traversed in </param>
        ///<param name="stopEvaluator">
        ///           the client hook for limiting the traversal size </param>
        ///<param name="returnableEvaluator">
        ///           the client hook for evaluating nodes before they are returned </param>
        ///<exception cref="IllegalArgumentException">
        ///            if one or more of the parameters are invalid </exception>
        public Traverser CreateTraverser(Order traversalOrder, Node startNode, RelationshipType traversableRel, Direction direction, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator)
        {
            RelationshipType[] traversableRels = new RelationshipType[] { traversableRel };
            Direction[] traversableDirs = new Direction[] { direction };
            if (traversalOrder == Order.BREADTH_FIRST)
            {
                return new BreadthFirstTraverser(startNode, traversableRels, traversableDirs, null, null, stopEvaluator, returnableEvaluator, null);
            }
            else if (traversalOrder == Order.DEPTH_FIRST)
            {
                return new DepthFirstTraverser(startNode, traversableRels, traversableDirs, null, null, stopEvaluator, returnableEvaluator, null);
            }
            else
            {
                throw new IllegalArgumentException("Unknown traverser type: " + traversalOrder);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="traversalOrder"></param>
        /// <param name="startNode"></param>
        /// <param name="traversableRels"></param>
        /// <param name="traversableDirs"></param>
        /// <param name="stopEvaluator"></param>
        /// <param name="returnableEvaluator"></param>
        /// <param name="randomEvaluator"></param>
        /// <returns></returns>
        public Traverser CreateTraverser(Order traversalOrder, Node startNode, RelationshipType[] traversableRels, Direction[] traversableDirs, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, RandomEvaluator randomEvaluator)
        {
            if (traversableRels == null || traversableDirs == null)
            {
                throw new IllegalArgumentException("Using this constructor requires that traversable " + "relationships array and traversable directions array " + "isn't null: travRels[" + Arrays.ToString(traversableRels) + "] " + "travDirs[" + Arrays.ToString(traversableDirs) + "]");
            }
            if (traversableRels.Length != traversableDirs.Length)
            {
                throw new IllegalArgumentException("Length of traversable relationships array isn't equal to " + "length of traversable directions array: " + "travRels.length[" + traversableRels.Length + "] != " + "travDirs.length[" + traversableDirs.Length + "]");
            }
            if (traversalOrder == Order.BREADTH_FIRST)
            {
                return new BreadthFirstTraverser(startNode, traversableRels, traversableDirs, null, null, stopEvaluator, returnableEvaluator, randomEvaluator);
            }
            else if (traversalOrder == Order.DEPTH_FIRST)
            {
                return new DepthFirstTraverser(startNode, traversableRels, traversableDirs, null, null, stopEvaluator, returnableEvaluator, randomEvaluator);
            }
            else
            {
                throw new IllegalArgumentException("Unknown traverser type: " + traversalOrder);
            }
        }

        #endregion
	}
}